//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#include "NjExample01.h"
#include "NjPulseEventTest.h"
#include "NjSynchronizationTest.h"
#include "NjSharedSyncObjectTest.h"
#include "NjDebugSystemTestImplementation.h"
#include "NjSharedSyncObjectConstants.h"
#include "NjThreadsSystem.h"
#include "NjTestUnit.h"
#include "NjRandUint.h"
#include "NjExampleRandGenerator.h"
#include "NjMemUtils.h"
#include "NjPlatformSyncObject.h"
#include "NjHighResolutionTimer.h"
#include "NjPlacementNew.h"

//----------------------------------------------------------------------------
class NjPulseEventTest::Static
{
public:
	typedef NjSharedSyncObjectConstants::Query<NjPulseEvent> Constants;

	static void RunPulseEventFunctional();
	static void RunPulseEventFunctionalMultithreaded();
	static void RunPulseEventPerformances();
	static void RunPulseEventAssert();
	static void RunPulseEventFunctionalThread(void * pData);
	static void RunPulseEventAssertThread(void * pData);

	enum ErrorState
	{
		STATUS_SUCCESS,
		NOT_INITIALIZED
	};

	class PulseEventFunctionalSharedData
	{
	public:
		NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(PulseEventFunctionalSharedData);
		NJ_TRIVIAL_DESTRUCTOR(PulseEventFunctionalSharedData);

		static const NjUint TOT_THREADS = 20;
		static const NjUint TOT_PULSE_EVENTS = 5;
		static const NjUint ITERATIONS = 100*NjSharedSyncObjectConstants::ITERATIONS_FACTOR;
		static const NjUint TOT_ENTRIES = ITERATIONS*TOT_THREADS*2;

		class ThreadData
		{
		public:
			NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(ThreadData);
			NJ_TRIVIAL_DESTRUCTOR(ThreadData);

			PulseEventFunctionalSharedData * m_pSharedData;
			NjThreadsSystem::Thread * m_pThread;
			ErrorState m_eErrorState;

			NJ_DISABLE_COPY(ThreadData);
		};

		NjPulseEvent m_aPulseEvent[TOT_PULSE_EVENTS];
		ThreadData m_aThreadsData[TOT_THREADS];
		NjAtomic<NjUint> m_kTotAliveThreads;
		NjAtomic<NjUint> m_kTotWaitingThreads;

		enum EventState
		{
			TRY_WAIT_START,
			WAIT_START,
			WAIT_END,
			WAIT_FAIL,
			WAKE_ONE_START,
			WAKE_ONE_END,
			WAKE_ALL_START,
			WAKE_ALL_END
		};

		class Entry
		{
		public:
			NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(Entry);
			NJ_TRIVIAL_DESTRUCTOR(Entry);

			NjUint m_uPulseEvent;
			EventState m_eEventState;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
			NjUint8 m_aSnapshot[sizeof(NjPulseEvent)];
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
			ThreadData * m_pThreadData;

			NJ_DISABLE_COPY(Entry);
		};

		Entry m_aEntries[TOT_ENTRIES];
		NjAtomic<NjUint> m_kTotEntries;

		NJ_DISABLE_COPY(PulseEventFunctionalSharedData);
	};

	class SharedData02
	{
	public:
		NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(SharedData02);
		NJ_TRIVIAL_DESTRUCTOR(SharedData02);

		NjSemaphore m_kWaitLock;
		NjSemaphore m_kStartUnlock;
		NjPlatformSyncObject::SyncObject * m_pPlatformSync;
		NjUint m_uTotal;

		static void ThreadFunct(void * pData);
	};

	NJ_DISABLE_INSTANCE(Static);
};
//----------------------------------------------------------------------------
void NjPulseEventTest::Run(void)
{
	Static::RunPulseEventFunctional();
//	Static::RunPulseEventFunctionalMultithreaded();
	Static::RunPulseEventPerformances();
	Static::RunPulseEventAssert();
}
//----------------------------------------------------------------------------
void NjPulseEventTest::Static::RunPulseEventFunctional()
{
	NJ_TEST_UNITS_GROUP(NjSharedSyncObjectConstants::Query<NjPulseEvent>::ms_szSyncObject);
		NJ_TEST_UNIT("Constructors, Destructors, WakeOne, WakeAll, TryWait");
			NjPulseEvent kPulseEvent;

			NJ_TEST(!kPulseEvent.TryWait(NjEventsSystem::NO_WAIT));

			kPulseEvent.WakeOne();

			NJ_TEST(!kPulseEvent.TryWait(NjEventsSystem::NO_WAIT));

			kPulseEvent.WakeAll();

			NJ_TEST(!kPulseEvent.TryWait(NjEventsSystem::NO_WAIT));
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
void NjPulseEventTest::Static::RunPulseEventFunctionalMultithreaded()
{
	NJ_TEST_UNITS_GROUP(NjSharedSyncObjectConstants::Query<NjPulseEvent>::ms_szSyncObject);
		NJ_TEST_UNIT("Constructors, Destructors, WakeOne, WakeAll, Wait, TryWait");
			PulseEventFunctionalSharedData * const pSharedData(NjNew(PulseEventFunctionalSharedData));

			pSharedData->m_kTotAliveThreads.Set(PulseEventFunctionalSharedData::TOT_THREADS);
			pSharedData->m_kTotWaitingThreads.Set(0);
			pSharedData->m_kTotEntries.Set(0);

			PulseEventFunctionalSharedData::ThreadData * pPos(pSharedData->m_aThreadsData);
			PulseEventFunctionalSharedData::ThreadData * const pEnd(pPos+PulseEventFunctionalSharedData::TOT_THREADS);

			while (pPos!=pEnd)
			{
				PulseEventFunctionalSharedData::ThreadData & kThreadData(*pPos);
				++pPos;

				kThreadData.m_pSharedData=pSharedData;
				kThreadData.m_pThread=NjThreadsSystem::CreateThread(RunPulseEventFunctionalThread, &kThreadData);

				kThreadData.m_eErrorState=NOT_INITIALIZED;

				NJ_TEST(kThreadData.m_pThread!=NJ_NULL);
			}

			pPos=pSharedData->m_aThreadsData;

			while (pPos!=pEnd)
			{
				PulseEventFunctionalSharedData::ThreadData & kThreadData(*pPos);
				++pPos;

				NJ_TEST(NjThreadsSystem::TryJoinThread(kThreadData.m_pThread, (pEnd+1-pPos)*PulseEventFunctionalSharedData::ITERATIONS*NjSharedSyncObjectConstants::TIME_GRANULARITY)==NjEventsSystem::TRY_SUCCESS);
				NJ_TEST(NjThreadsSystem::DestroyThread(kThreadData.m_pThread));
				NJ_TEST(kThreadData.m_eErrorState==STATUS_SUCCESS);
			}

			PulseEventFunctionalSharedData::Entry const * pEntry(pSharedData->m_aEntries);
			PulseEventFunctionalSharedData::Entry const * const pEntryEnd(pEntry+pSharedData->m_kTotEntries.Get());

			NjPulseEvent * aOwnedPulseEvent[PulseEventFunctionalSharedData::TOT_THREADS];
			NjMemUtils::Zero(aOwnedPulseEvent, sizeof(aOwnedPulseEvent));

			NjUint aWaitingOnPulseEvent[PulseEventFunctionalSharedData::TOT_PULSE_EVENTS];
			NjUint aWakenOnPulseEvent[PulseEventFunctionalSharedData::TOT_PULSE_EVENTS];
			NjMemUtils::Zero(aWaitingOnPulseEvent, sizeof(aWaitingOnPulseEvent));
			NjMemUtils::Zero(aWakenOnPulseEvent, sizeof(aWakenOnPulseEvent));

			while (pEntry<pEntryEnd)
			{
				NjUint const uThread(pEntry->m_pThreadData-pSharedData->m_aThreadsData);
				NJ_TEST(uThread<PulseEventFunctionalSharedData::TOT_THREADS);

				NjUint const uPulseEvent(pEntry->m_uPulseEvent);
				NJ_TEST(uPulseEvent<PulseEventFunctionalSharedData::TOT_PULSE_EVENTS);

				NjPulseEvent * const pPulseEvent(pSharedData->m_aPulseEvent+uPulseEvent);

				switch(pEntry->m_eEventState)
				{
					case PulseEventFunctionalSharedData::TRY_WAIT_START:
					case PulseEventFunctionalSharedData::WAIT_START:
						NJ_TEST(aOwnedPulseEvent[uThread]==NJ_NULL);
						aOwnedPulseEvent[uThread]=pPulseEvent;
						++aWaitingOnPulseEvent[uPulseEvent];
						break;

					case PulseEventFunctionalSharedData::WAIT_END:
						NJ_TEST(aWakenOnPulseEvent[uPulseEvent]>0);
						--aWakenOnPulseEvent[uPulseEvent];

					case PulseEventFunctionalSharedData::WAIT_FAIL:
						NJ_TEST(aOwnedPulseEvent[uThread]==pPulseEvent);
						aOwnedPulseEvent[uThread]=NJ_NULL;

						NJ_TEST(aWaitingOnPulseEvent[uPulseEvent]>0);
						--aWaitingOnPulseEvent[uPulseEvent];
						break;

					case PulseEventFunctionalSharedData::WAKE_ONE_START:
						NJ_TEST(aOwnedPulseEvent[uThread]==NJ_NULL);

						if (aWakenOnPulseEvent[uPulseEvent]<aWaitingOnPulseEvent[uPulseEvent])
							++aWakenOnPulseEvent[uPulseEvent];
						break;

					case PulseEventFunctionalSharedData::WAKE_ALL_START:
						NJ_TEST(aOwnedPulseEvent[uThread]==NJ_NULL);

						aWakenOnPulseEvent[uPulseEvent]=aWaitingOnPulseEvent[uPulseEvent];
						break;

					case PulseEventFunctionalSharedData::WAKE_ONE_END:
					case PulseEventFunctionalSharedData::WAKE_ALL_END:
						NJ_TEST(aOwnedPulseEvent[uThread]==NJ_NULL);
						break;

					default:
						NJ_TEST_FAIL();
				}
				++pEntry;
			}
			NjDelete(pSharedData);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
void NjPulseEventTest::Static::RunPulseEventPerformances()
{
	NjSharedSyncObjectTest<NjPulseEvent>::RunPerformancesConstructor();

	NJ_TEST_UNITS_GROUP(NjSharedSyncObjectConstants::Query<NjPulseEvent>::ms_szSyncObject);
		NJ_TEST_UNIT("WakeOne, WakeAll, TryWait (performances)");
			static const NjUint OPERATIONS = 4000000*NjSharedSyncObjectConstants::ITERATIONS_FACTOR;
			static const NjUint ITERATIONS = 200*NjSharedSyncObjectConstants::ITERATIONS_FACTOR;
			static NjBool s_bResult=NJ_FALSE;

			NjUint * const pOperations(NjNewArray(NjUint[ITERATIONS]));

			NjUint uTotOperations(OPERATIONS);
			NjRandUint<EXAMPLE_RAND_GENERATOR> kRand(0, 21);
			NjUint uIteration(ITERATIONS);
			NjUint uMaxOperations(0);
			while (uIteration>0)
			{
				NjUint uOperations(uTotOperations/uIteration);
				--uIteration;

				if (uIteration>0)
					uOperations+=(NjInt(uOperations)*NjInt(kRand()-10))/100;

				if (uOperations>uMaxOperations)
					uMaxOperations=uOperations;

				pOperations[uIteration]=uOperations;
				uTotOperations-=uOperations;
			}

			NjUint8 * const pBuffer(NjNewArray(NjUint8[(sizeof(NjPulseEvent)+sizeof(NjPlatformSyncObject::SyncObject)*2)*uMaxOperations]));
			NjPulseEvent * const pPulseEvents(reinterpret_cast<NjPulseEvent *>(pBuffer));
			NjPlatformSyncObject::SyncObject * const pPlatformSyncObjects(reinterpret_cast<NjPlatformSyncObject::SyncObject *>(pPulseEvents+uMaxOperations));

			SharedData02 kSharedData;
			kSharedData.m_pPlatformSync=pPlatformSyncObjects+uMaxOperations;
			kSharedData.m_uTotal=uMaxOperations;

			{
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uMaxOperations*2);
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Initialize(*pPlatformPos);
					++pPlatformPos;
				}

				NjPulseEvent * pPulseEventPos(pPulseEvents);
				NjPulseEvent const * const pPulseEventEnd(pPulseEventPos+uMaxOperations);
				while (pPulseEventPos<pPulseEventEnd)
				{
					NjPlacementNew kPlacement(pPulseEventPos);
					NJ_PLACEMENT_NEW(kPlacement, NjPulseEvent);
					++pPulseEventPos;
				}
			}

			NjThreadsSystem::Thread * const pThread(NjThreadsSystem::CreateThread(SharedData02::ThreadFunct, &kSharedData));
			NJ_TEST(pThread!=NJ_NULL);
			kSharedData.m_kWaitLock.Lock();

			NjHighResolutionTimer kPlatformSyncObjectTryLockFail;
			NjHighResolutionTimer kPlatformSyncObjectUnlock;
			NjHighResolutionTimer kPulseEventWakeOne;
			NjHighResolutionTimer kPulseEventWakeAll;
			NjHighResolutionTimer kPulseEventTryWaitFail;
			NjHighResolutionTimer kOverHead;

			NjUint const * pPos(pOperations);
			NjUint const * const pEnd(pOperations+ITERATIONS);

			while (pPos!=pEnd)
			{
				NjUint const uOperations(*pPos);
				NjPulseEvent * pPulseEventPos(pPulseEvents);
				NjPulseEvent const * const pPulseEventEnd(pPulseEventPos+uOperations);
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uOperations);
				NjPlatformSyncObject::SyncObject * pPlatformTryLockFailPos(pPlatformSyncObjects+uMaxOperations);
				NjPlatformSyncObject::SyncObject const * const pPlatformTryLockFailEnd(pPlatformTryLockFailPos+uOperations);

				kOverHead.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					++pPlatformPos;
				}
				kOverHead.Pause();

				pPlatformPos=pPlatformTryLockFailPos;
				kPlatformSyncObjectTryLockFail.Start();
				while (pPlatformPos<pPlatformTryLockFailEnd)
				{
					s_bResult=NjPlatformSyncObject::TryLock(*pPlatformPos, 0);
					++pPlatformPos;
				}
				kPlatformSyncObjectTryLockFail.Pause();
				
				NJ_TEST(!s_bResult);

				pPlatformPos=pPlatformSyncObjects;
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Lock(*pPlatformPos);
					++pPlatformPos;
				}

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectUnlock.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Unlock(*pPlatformPos);
					++pPlatformPos;
				}
				kPlatformSyncObjectUnlock.Pause();

				kPulseEventTryWaitFail.Start();
				while (pPulseEventPos<pPulseEventEnd)
				{
					s_bResult=pPulseEventPos->TryWait(0);
					++pPulseEventPos;
				}
				kPulseEventTryWaitFail.Pause();

				NJ_TEST(!s_bResult);

				pPulseEventPos=pPulseEvents;
				kPulseEventWakeOne.Start();
				while (pPulseEventPos<pPulseEventEnd)
				{
					pPulseEventPos->WakeOne();
					++pPulseEventPos;
				}
				kPulseEventWakeOne.Pause();

				pPulseEventPos=pPulseEvents;
				kPulseEventWakeAll.Start();
				while (pPulseEventPos<pPulseEventEnd)
				{
					pPulseEventPos->WakeAll();
					++pPulseEventPos;
				}
				kPulseEventWakeAll.Pause();

				++pPos;
			}

			kSharedData.m_kStartUnlock.Unlock();
			NJ_TEST(NjThreadsSystem::JoinThread(pThread));
			NJ_TEST(NjThreadsSystem::DestroyThread(pThread));

			{
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uMaxOperations*2);
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Destroy(*pPlatformPos);
					++pPlatformPos;
				}

				NjPulseEvent * pPulseEventPos(pPulseEvents);
				NjPulseEvent const * const pPulseEventEnd(pPulseEvents+uMaxOperations);
				while (pPulseEventPos<pPulseEventEnd)
				{
					pPulseEventPos->~NjPulseEvent();
					++pPulseEventPos;
				}

			}

			NjDeleteArray(pBuffer);
			NjDeleteArray(pOperations);

			NjFloat fOverHead(kOverHead.GetTimeS());
			NjFloat fPlatformSyncObjectTryLockFail(kPlatformSyncObjectTryLockFail.GetTimeS()-fOverHead);
			NjFloat fPlatformSyncObjectUnlock(kPlatformSyncObjectUnlock.GetTimeS()-fOverHead);
			NjFloat fPulseEventTryWaitFail(kPulseEventTryWaitFail.GetTimeS()-fOverHead);
			NjFloat fPulseEventWakeOne(kPulseEventWakeOne.GetTimeS()-fOverHead);
			NjFloat fPulseEventWakeAll(kPulseEventWakeAll.GetTimeS()-fOverHead);

			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_UNLOCK, OPERATIONS, fPlatformSyncObjectUnlock);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::METH_WAKE_ONE, OPERATIONS, fPulseEventWakeOne);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::METH_WAKE_ALL, OPERATIONS, fPulseEventWakeAll);
			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_TRY_LOCK_FAIL, OPERATIONS, fPlatformSyncObjectTryLockFail);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::METH_TRY_WAIT_FAIL, OPERATIONS, fPulseEventTryWaitFail);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
void NjPulseEventTest::Static::RunPulseEventAssert()
{
#if NJ_PULSE_EVENT_STORE_USERS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
	NJ_TEST_UNITS_GROUP(NjSharedSyncObjectConstants::Query<NjPulseEvent>::ms_szSyncObject);
		NJ_TEST_UNIT("Assertion (destruction)");
			NjSmartPtr<NjDebugSystem::Implementation> spImplementation(NjDebugSystem::GetImplementation());
			NjDebugSystemTestImplementation * const pTestBreakpoint(NjNew(NjDebugSystemTestImplementation(spImplementation)));
			NJ_TEST(pTestBreakpoint!=NJ_NULL);
			NjDebugSystem::SetImplementation(pTestBreakpoint);

			NjThreadsSystem::Thread * pThread;

			{
				NjPulseEvent kSyncObject;
				NJ_TEST(!pTestBreakpoint->WasTriggeredBreakpoint());

				pThread=NjThreadsSystem::CreateThread(RunPulseEventAssertThread, &kSyncObject);

				NJ_TEST(pThread!=NJ_NULL);
				NJ_TEST(NjThreadsSystem::Sleep(NjSharedSyncObjectConstants::TIME_GRANULARITY));
			}

			NJ_TEST(pTestBreakpoint->WasTriggeredBreakpoint());

			NJ_TEST(NjThreadsSystem::TryJoinThread(pThread, NjSharedSyncObjectConstants::TIME_GRANULARITY*5)==NjEventsSystem::TRY_SUCCESS);
			NJ_TEST(NjThreadsSystem::DestroyThread(pThread));

			NjDebugSystem::SetImplementation(spImplementation);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
#endif // NJ_PULSE_EVENT_STORE_USERS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
}
//----------------------------------------------------------------------------
void NjPulseEventTest::Static::RunPulseEventFunctionalThread(void * pData)
{
	PulseEventFunctionalSharedData::ThreadData & kThreadData(*reinterpret_cast<PulseEventFunctionalSharedData::ThreadData *>(pData));
	PulseEventFunctionalSharedData & kSharedData(*kThreadData.m_pSharedData);
	NjRandUint<EXAMPLE_RAND_GENERATOR> kRand(0, 1000000);
	static const NjUint NO_WAIT_THREADS_MARGIN = 2;

	NjUint uIterations(PulseEventFunctionalSharedData::ITERATIONS);
	NjBool bThreadAlive(NJ_TRUE);

	while (uIterations>0)
	{
		--uIterations;

		NjUint const uPulseEvent(kRand()%PulseEventFunctionalSharedData::TOT_PULSE_EVENTS);
		NjUint uAction;
		
		if (uIterations<=(kSharedData.m_kTotAliveThreads.Get()+NO_WAIT_THREADS_MARGIN))
		{
			NjThreadsSystem::Yeld();

			if (bThreadAlive)
			{
				bThreadAlive=NJ_FALSE;
				kSharedData.m_kTotAliveThreads.Decrement();
			}

			uAction=3;
		}
		else if (uIterations<=100)
			uAction=1+(kRand()%3);
		else
			uAction=kRand()%4;

		for(;;)
		{
			if (uAction!=0)
				break;

			NjUint uTotWaitingThreads(kSharedData.m_kTotWaitingThreads.Get());
			NjBool bRetry;

			for(;;)
			{
				if (kSharedData.m_kTotAliveThreads.Get()<uTotWaitingThreads+NO_WAIT_THREADS_MARGIN)
				{
					bRetry=NJ_TRUE;
					break;
				}

				NjUint const uOldTotWaitingThreads(kSharedData.m_kTotWaitingThreads.PostCompareExchange(uTotWaitingThreads, uTotWaitingThreads+1));

				if (uOldTotWaitingThreads==uTotWaitingThreads)
				{
					bRetry=NJ_FALSE;
					break;
				}

				uTotWaitingThreads=uOldTotWaitingThreads;
			}

			if (!bRetry)
				break;

			uAction=kRand()%4;
		}

		NjPulseEvent * const pPulseEvent(kSharedData.m_aPulseEvent+uPulseEvent);

		NjUint uIndex(kSharedData.m_kTotEntries.PostIncrement());
		PulseEventFunctionalSharedData::Entry * pEntry(kSharedData.m_aEntries+uIndex);
		pEntry->m_uPulseEvent=uPulseEvent;
		pEntry->m_pThreadData=&kThreadData;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
		NjMemUtils::Copy(pEntry->m_aSnapshot, sizeof(pEntry->m_aSnapshot), pPulseEvent, sizeof(NjPulseEvent));
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT

		PulseEventFunctionalSharedData::EventState eNextState(PulseEventFunctionalSharedData::WAIT_END);

		if (uAction==0)
		{
			pEntry->m_eEventState=PulseEventFunctionalSharedData::WAIT_START;

			if (kSharedData.m_kTotAliveThreads.Get()<kSharedData.m_kTotWaitingThreads.Get()+NO_WAIT_THREADS_MARGIN-1)
			{
				pEntry->m_eEventState=PulseEventFunctionalSharedData::TRY_WAIT_START;
				if (!pPulseEvent->TryWait(NjSharedSyncObjectConstants::TIME_GRANULARITY))
					eNextState=PulseEventFunctionalSharedData::WAIT_FAIL;
			}
			else
			{
				pPulseEvent->Wait();
				kSharedData.m_kTotWaitingThreads.Decrement();
			}
		}
		else if (uAction==1)
		{
			pEntry->m_eEventState=PulseEventFunctionalSharedData::TRY_WAIT_START;
			if (!pPulseEvent->TryWait(NjSharedSyncObjectConstants::TIME_GRANULARITY))
				eNextState=PulseEventFunctionalSharedData::WAIT_FAIL;
		}
		else if (uAction==2)
		{
			pEntry->m_eEventState=PulseEventFunctionalSharedData::WAKE_ONE_START;
			pPulseEvent->WakeOne();
			eNextState=PulseEventFunctionalSharedData::WAKE_ONE_END;
		}
		else
		{
			NJ_ASSERT(uAction==3);
			pEntry->m_eEventState=PulseEventFunctionalSharedData::WAKE_ALL_START;
			pPulseEvent->WakeAll();
			eNextState=PulseEventFunctionalSharedData::WAKE_ALL_END;
		}

		uIndex=kSharedData.m_kTotEntries.PostIncrement();
		pEntry=kSharedData.m_aEntries+uIndex;
		pEntry->m_uPulseEvent=uPulseEvent;
		pEntry->m_pThreadData=&kThreadData;
		pEntry->m_eEventState=eNextState;
#if NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
		NjMemUtils::Copy(pEntry->m_aSnapshot, sizeof(pEntry->m_aSnapshot), pPulseEvent, sizeof(NjPulseEvent));
#endif // NJ_SYNCHRONIZATION_TEST_SNAPSHOT_SYNC_OBJECT
	}

	kThreadData.m_eErrorState=STATUS_SUCCESS;
}
//----------------------------------------------------------------------------
void NjPulseEventTest::Static::RunPulseEventAssertThread(void * pData)
{
	NjPulseEvent * const pSyncObject(reinterpret_cast<NjPulseEvent *>(pData));
	pSyncObject->TryWait(NjSharedSyncObjectConstants::TIME_GRANULARITY*2);
}
//----------------------------------------------------------------------------
void NjPulseEventTest::Static::SharedData02::ThreadFunct(void * pData)
{
	SharedData02 * const pSharedData(reinterpret_cast<SharedData02 *>(pData));
	if (pSharedData==NJ_NULL)
		return;

	NjPlatformSyncObject::SyncObject * pPlatformPos(pSharedData->m_pPlatformSync);
	NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+pSharedData->m_uTotal);
	while (pPlatformPos<pPlatformEnd)
	{
		NjPlatformSyncObject::Lock(*pPlatformPos);
		++pPlatformPos;
	}

	pSharedData->m_kWaitLock.Unlock();
	pSharedData->m_kStartUnlock.Lock();

	pPlatformPos=pSharedData->m_pPlatformSync;
	while (pPlatformPos<pPlatformEnd)
	{
		NjPlatformSyncObject::Unlock(*pPlatformPos);
		++pPlatformPos;
	}
}
//----------------------------------------------------------------------------
